package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"

	"time"

	"io/ioutil"

	//datastore "github.com/ipfs/go-datastore"

	//"github.com/filecoin-project/lotus/chain/actors/adt"

	"os"

	// excelize "github.com/360EntSecGroup-Skylar/excelize/v2"
	// "github.com/filecoin-project/go-address"
	"github.com/filecoin-project/go-bitfield"
	"github.com/filecoin-project/specs-actors/v7/actors/builtin/miner"
	"github.com/ipfs/go-cid"

	// miner "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
	"github.com/filecoin-project/lotus/chain/types"
	// min "github.com/filecoin-project/specs-actors/actors/builtin/miner"
)

type Epoch struct {
	BlockNumber int
	BlockLength []int
	// Block         []types.BlockHeader
	Tipset        int32
	MessageNumber int
	MessageLength []int
	// Message       []types.Message
	//ErrorPartion  int64
	//TotalPartion  int64
}

type EP struct {
	Block   []types.BlockHeader
	Message []types.Message
	Tipset  int32
}

func ReadFile(path1 string, path2 string) ([5000]EP, error) {

	var m types.Message
	var b types.BlockHeader
	//var count int
	var temp int64
	var temp2 int64
	var ep [50000]Epoch
	var ep2 [5000]EP
	temp = 0
	temp2 = 0
	fm, err1 := ioutil.ReadFile(path1) //元数据
	ml := int64(len(fm))
	fd, err2 := ioutil.ReadFile(path2)
	// f, _ := os.OpenFile(path2, os.O_RDONLY, os.ModePerm)
	// n, _ := f.Seek(0, os.SEEK_END)
	if err1 != nil || err2 != nil {
		fmt.Println("err ReadFile")
		return ep2, err1
	}
	fmt.Println(ml)

	for i := 0; i < 50000; i++ {
		ep[i].Tipset = int32(200000 - i) //写入的时候从575000开始

		bn, _ := byte2int(fm[temp : temp+2]) //该tipset的区块个数
		ep[i].BlockNumber = bn
		temp = temp + 2
		for j := 0; j < bn; j++ {
			bl, _ := byte2int(fm[temp : temp+2])
			ep[i].BlockLength = append(ep[i].BlockLength, bl)
			temp = temp + 2
		}

		mn, _ := byte2int(fm[temp : temp+2]) //该tipset的消息个数
		ep[i].MessageNumber = mn
		temp = temp + 2
		for j := 0; j < mn; j++ { //每个消息的长度
			ml, _ := byte2int(fm[temp : temp+2])
			ep[i].MessageLength = append(ep[i].MessageLength, ml)
			temp = temp + 2
		}
		if temp >= ml {
			break
		}
	}

	// TODO:
	temp2 = int64(len(fd))
	for i := 49999; i >= 0; i-- {
		//fmt.Println(ep[i].Tipset) //
		//fmt.Println(ep[i].MessageNumber)
		var tipset int32

		var ms []types.Message
		for j := ep[i].MessageNumber - 1; j >= 0; j-- {
			buf := bytes.NewReader(fd[temp2-int64(ep[i].MessageLength[j]) : temp2])
			m.UnmarshalCBOR(buf)
			temp2 = temp2 - int64(ep[i].MessageLength[j])
			if m.Method != 0 {
				ms = append(ms, m)
			}
			//fmt.Println(m.Cid()) //
		}
		ep2[i%5000].Message = ms

		var bs []types.BlockHeader
		for j := ep[i].BlockNumber - 1; j >= 0; j-- {
			buf := bytes.NewReader(fd[temp2-int64(ep[i].BlockLength[j]) : temp2])
			b.UnmarshalCBOR(buf)
			temp2 = temp2 - int64(ep[i].BlockLength[j])
			bs = append(bs, b)
			//fmt.Println(b.Cid()) //
			tipset = int32(b.Height)
		}
		ep2[i%5000].Block = bs

		ep2[i%5000].Tipset = tipset

		fmt.Println(ep2[i%5000].Tipset)
		if i%5000 == 0 {
			classify(ep2)
		}
	}

	fd = nil
	fm = nil
	fmt.Println("结束解读")
	return ep2, nil
}

func writeblock(data chan types.BlockHeader) {
	f, err := os.OpenFile(path+"block 2"+"/"+"block.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println("写区块 打开文件", err)
	}
	defer f.Close()
	w := bufio.NewWriter(f)
	for {
		block := <-data
		if block.Height == 0 {
			fmt.Println("结束写区块")
			break
		}
		d, err := json.Marshal(block)
		if err != nil {
			fmt.Println("写区块时", err)
		}
		nn, err := w.Write(d)
		if err != nil {
			fmt.Println("写区块 write", nn, err)
		}

		w.WriteString("\n")
	}
}

type wm struct {
	C cid.Cid
	M types.Message
	H int32
}

func writemessage(data chan wm) {
	MinerId := make(map[string]bool)
	for {
		message := <-data
		if message.H == 0 {
			fmt.Println("消息结束")
			break
		}
		if message.M.Method == 0 {
			continue
		}
		if message.M.Method > 25 {
			continue
		}

		key := message.M.To.String() + "_" + MethodsMiner[message.M.Method]
		_, ok := MinerId[key]
		f1 := message.M.To.String()
		f2 := MethodsMiner[message.M.Method]
		p := path + "miner" + "/" + f1 + "/" + f2 + ".txt"
		if !ok {
			MinerId[key] = true
			os.Mkdir(path+"miner"+"/"+f1, 0777)
			os.Chmod(path+"miner"+"/"+f1, 0777)
		}
		v, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) //打开文件
		if err != nil {
			fmt.Println("写消息 打开文件", err)
		}
		val := bufio.NewWriter(v)
		d, err := json.Marshal(message)
		if err != nil {
			fmt.Println("写消息 marshal", err)
		}
		nn, err := val.Write(d)
		if err != nil {
			fmt.Println("写消息 write", nn, err)
		}

		val.WriteString("\n")
		err = val.Flush()
		if err != nil {
			fmt.Println("写消息 flush", err)
		}
		v.Close()
	}
}

// OpenFile 判断文件是否存在  存在则OpenFile 不存在则Create
func OpenFile(f1 string, f2 string) (*os.File, error) {
	p := path + "miner" + "/" + f1 + "/" + f2 + ".txt"
	if _, err := os.Stat(p); os.IsNotExist(err) {
		os.Mkdir(path+"miner"+"/"+f1, 0777)
		os.Chmod(path+"miner"+"/"+f1, 0777)
		return os.OpenFile(p, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) //打开文件
		//创建文件
	}
	return os.OpenFile(p, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) //打开文件
}

func classify(ep [5000]EP) {

	block := make(chan types.BlockHeader, 10)
	message := make(chan wm, 100)
	go writeblock(block)
	go writemessage(message)
	for i := 4999; i >= 0; i-- {
		if ep[i].Tipset == 0 {
			continue
		}
		for j := 0; j < len(ep[i].Block); j++ {
			block <- ep[i].Block[j]

		}
		for j := 0; j < len(ep[i].Message); j++ {
			m := wm{M: ep[i].Message[j], H: ep[i].Tipset, C: ep[i].Message[j].Cid()}
			message <- m

		}
		ep[i] = EP{}
	}
	block <- types.BlockHeader{Height: 0}
	message <- wm{H: 0}

}

func main() {

	// ReadFile("/home/zhujerry/.lotus/meta550000", "/home/zhujerry/.lotus/data550000")

	// files, err := ioutil.ReadDir(`/home/zhujerry/.lotus/miner`)
	// if err != nil {
	// 	panic(err)
	// }
	// // 获取文件，并输出它们的名字
	// for _, file := range files {
	// 	println(file.Name())
	// }
	// files, err := ioutil.ReadDir(`/home/zhujerry/.lotus/miner/f02731`)
	// if err != nil {
	// 	panic(err)
	// }
	// for _, file := range files {
	// 	println(file.Name())
	// }

	// file, err := ioutil.ReadFile("/home/zhujerry/.lotus/miner/f02731")
	// if err != nil {
	// 	fmt.Println(err)
	// }

	var tipset [550000][]wm
	var m Miner
	m.name = "f01012"
	m.bitmap = bitfield.New()
	m.errorbitmp = bitfield.New()
	m.recoverbitmp = bitfield.New()
	file, err := os.Open("/home/zhujerry/.lotus/miner/" + m.name + "/ProveCommitSector.txt")
	if err != nil {
		fmt.Println(err)
	}
	buf := bufio.NewScanner(file)
	// var params miner.ProveCommitSectorParams

	for {
		if !buf.Scan() {
			break
		}
		line := buf.Text()
		var message wm
		json.Unmarshal([]byte(line), &message)
		tipset[message.H] = append(tipset[message.H], message)
		// params.UnmarshalCBOR(bytes.NewReader(message.M.Params))
		// var s Sector
		// s.Height = int(message.H)
		// s.Number = int(params.SectorNumber)
		// f02731.append(s)
	}
	// fmt.Println(f02731.Count)

	file, err = os.Open("/home/zhujerry/.lotus/miner/" + m.name + "/DeclareFaults.txt")
	if err != nil {
		fmt.Println(err)
	}
	buf = bufio.NewScanner(file)
	// var params2 miner.DeclareFaultsParams
	// var FaultCount uint64
	for {
		if !buf.Scan() {
			break
		}
		line := buf.Text()
		var message wm
		json.Unmarshal([]byte(line), &message)
		tipset[message.H] = append(tipset[message.H], message)
		// params2.UnmarshalCBOR(bytes.NewReader(message.M.Params))
		// for _, v := range params2.Faults {
		// 	a, err := v.Sectors.Count()
		// 	if err != nil {
		// 		fmt.Println(err)
		// 	}
		// 	FaultCount += a
		// }
	}
	// fmt.Println(FaultCount)

	file, err = os.Open("/home/zhujerry/.lotus/miner/" + m.name + "/DeclareFaultsRecovered.txt")
	if err != nil {
		fmt.Println(err)
	}
	buf = bufio.NewScanner(file)
	// var params3 miner.DeclareFaultsRecoveredParams
	// var FaultRecoveryCount uint64
	for {
		if !buf.Scan() {
			break
		}
		line := buf.Text()
		var message wm
		json.Unmarshal([]byte(line), &message)
		tipset[message.H] = append(tipset[message.H], message)
		// params3.UnmarshalCBOR(bytes.NewReader(message.M.Params))
		// for _, v := range params3.Recoveries {
		// 	a, err := v.Sectors.Count()
		// 	if err != nil {
		// 		fmt.Println(err)
		// 	}
		// 	FaultRecoveryCount += a
		// }
	}
	// fmt.Println(FaultRecoveryCount)

	var p1 miner.ProveCommitSectorParams
	var p2 miner.DeclareFaultsParams
	var p3 miner.DeclareFaultsRecoveredParams
	var p4 miner.SubmitWindowedPoStParams
	for i := 0; i < 550000; i++ {
		if len(tipset[i]) == 0 {
			continue
		}
		for _, v := range tipset[i] {
			if v.H > 72425 {
				break
			}
			if v.M.Method == 7 { //ProveCommitSector
				param := p1
				param.UnmarshalCBOR(bytes.NewReader(v.M.Params))
				sn := param.SectorNumber
				end, err := m.bitmap.IsSet(uint64(sn))
				if end || err != nil {
					continue
				}
				// if m.bitmap.length / 2349 == 0{
				// 	m.partition= append(m.partition, [48]Partition{})
				// }
				m.bitmap.Set(uint64(sn))

				// s := Sector{Number: uint64(sn), Height: uint64(v.H)}

			}
			if v.M.Method == 10 { //"DeclareFaults",
				param := p2
				param.UnmarshalCBOR(bytes.NewReader(v.M.Params))
				for _, v := range param.Faults {
					a, err := v.Sectors.All(10000000)
					if err != nil {
						fmt.Println(err)
					}
					for _, vv := range a {
						m.errorbitmp.Set(vv)
					}
				}
			}
			if v.M.Method == 11 { //"DeclareRecoverryFaults",
				param := p3
				param.UnmarshalCBOR(bytes.NewReader(v.M.Params))
				for _, v := range param.Recoveries {
					a, err := v.Sectors.All(10000000)
					if err != nil {
						fmt.Println(err)
					}
					for _, vv := range a {
						end, err := m.errorbitmp.IsSet(vv)
						if err != nil {
							fmt.Println(err)
						}
						if end {
							m.errorbitmp.Unset(vv)
						}
					}
				}
			}
			if v.M.Method == 5 { //wpost
				param := p4
				param.UnmarshalCBOR(bytes.NewReader(v.M.Params))
				for _, v := range param.Partitions {
					a, err := v.Skipped.All(1000000)
					if err != nil {
						fmt.Println(err)
					}
					for _, vv := range a {
						m.errorbitmp.Set(vv)
					}
				}
			}

		}
	}
	a, _ := m.bitmap.Count()
	b, _ := m.errorbitmp.Count()

	fmt.Println(a, b)

	time.Sleep(1 * time.Second)
	fmt.Println("______")

}

type Miner struct {
	sector       []Sector
	bitmap       bitfield.BitField //记录有哪些sector
	errorbitmp   bitfield.BitField
	recoverbitmp bitfield.BitField
	name         string
	partition    [][48]Partition
}

type Sector struct {
	Number uint64
	Height uint64
}

type Partition struct {
	Count        uint64
	InitSector   bitfield.BitField
	NormalSector bitfield.BitField
	FaultSector  bitfield.BitField
}

// type Sector struct {
// 	Number int
// 	Height int
// }
// type Deadline struct {
// 	Part  [48]Partition
// 	Count int
// }
// type Miner struct {
// 	Deadline []Deadline
// 	Name     []string
// 	Count    int
// }

// func (m *Miner) append(s Sector) {
// 	if len(m.Deadline) == 0 || m.Deadline[len(m.Deadline)-1].Count == 112752 {
// 		var d Deadline
// 		m.Deadline = append(m.Deadline, d)
// 	}
// 	m.Count++
// 	m.Deadline[len(m.Deadline)-1].append(s)
// }
// func (d *Deadline) append(s Sector) {
// 	d.Part[d.Count/2349][d.Count%2349] = s
// 	d.Count++
// }

// type Partition [2349]Sector

func byte2int(bs []byte) (int, error) {
	//var bs []byte
	var d int
	d = 0
	if len(bs) > 2 {
		err := fmt.Errorf("too large")
		return d, err
	}
	d = int(bs[1]) + d
	d = int(bs[0])*256 + d

	return d, nil
}

var MethodsMiner = []string{
	"send",
	"Constructor",
	"ControlAddresses",
	"ChangeWorkerAddress",
	"ChangePeerID",
	"SubmitWindowedPoSt",     //5
	"PreCommitSector",        //6
	"ProveCommitSector",      //7
	"ExtendSectorExpiration", //8
	"TerminateSectors",       //9
	"DeclareFaults",          //10
	"DeclareFaultsRecovered", //11
	"OnDeferredCronEvent",
	"CheckSectorProven",
	"ApplyRewards",
	"ReportConsensusFault",
	"WithdrawBalance",
	"ConfirmSectorProofsValid",
	"ChangeMultiaddrs",
	"CompactPartitions",
	"CompactSectorNumbers",
	"ConfirmUpdateWorkerKey",
	"RepayDebt",
	"ChangeOwnerAddress",
	"DisputeWindowedPoSt",
}

var path = "/home/zhujerry/.lotus/"
